<!DOCTYPE html>
<html>

<head includeDefault="true">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  <title>3D库图显示</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }

    #label {
      position: absolute;
      padding: 10px;
      background: rgba(255, 255, 255, 0.6);
      line-height: 1;
      border-radius: 5px;
    }

    #video {
      position: absolute;
      width: 0;
      height: 0;
    }
  </style>
  <script src="../ThreeJs/js/three.js"></script>
  <script src="../ThreeJs/js/stats.min.js"></script>
  <script src="../ThreeJs/js/DragControls.js"></script>
  <script src="../ThreeJs/js/OrbitControls.js"></script>
  <script src="../ThreeJs/js/FirstPersonControls.js"></script>
  <script src="../ThreeJs/js/TransformControls.js"></script>
  <script src="../ThreeJs/js/dat.gui.min.js"></script>
  <script src="../ThreeJs/js/EffectComposer.js"></script>
  <script src="../ThreeJs/js/RenderPass.js"></script>
  <script src="../ThreeJs/js/OutlinePass.js"></script>
  <script src="../ThreeJs/js/FXAAShader.js"></script>
  <script src="../ThreeJs/js/CopyShader.js"></script>
  <script src="../ThreeJs/js/ShaderPass.js"></script>
  <script src="../ThreeJs/js/ThreeBSP.js"></script>
  <script src="../ThreeJs/ThreeJs_Drag.js" charset="UTF-8"></script>
  <script src="../ThreeJs/ThreeJs_Composer.js" charset="UTF-8"></script>
  <script src="../ThreeJs/Modules.js" charset="UTF-8"></script>
  <script src="../ThreeJs/js/Tween.js"></script>
  <script src="../ThreeJs/js/jquery-1.11.0.min.js"></script>
  <script src="../echarts/echarts.min.js"></script>
  <script src="../ThreeJs/config.js"></script>
</head>

<body>
  <div id="label"></div>
  <div id="container"></div>
  <video id="video" autoplay loop muted>
    <source src="../video/videoPlane.mp4">
  </video>

  <script>

    var stats = initStats();
    var scene, camera, renderer, controls;

    /*
     * 初始化场景
     *
     * 场景就是一个三维空间。 用 [Scene] 类声明一个叫 [scene] 的对象。
     */
    function initScene() {
      // 创建场景对象Scene
      scene = new THREE.Scene();
    }

    /*
     * 初始化相机
     *
     * threejs常用的相机对象就是正投影相机OrthographicCamera和透视投影相机PerspectiveCamera
     * OpenGL（WebGL）中，三维空间中的物体投影到二维空间的方式中，存在透视投影和正投影两种相机
     * 透视投影就是、从视点开始越近的物体越大、远处的物体绘制的较小的一种方式、和日常生活中我们看物体的方式是一致的
     * 正投影就是不管物体和视点距离，都按照统一的大小进行绘制、在建筑和设计等领域需要从各个角度来绘制物体
     * 因此这种投影被广泛应用。在 Three.js 也能够指定透视投影和正投影两种方式的相机
     *
     * 本文按照以下的步骤设置透视投影方式：
     * 1、声明全局的变量（对象）
     * 2、设置透视投影的相机
     * 3、设置相机的位置坐标
     * 4、设置相机的上为「z」轴方向
     * 5、设置视野的中心坐标
     */
    function initCamera() {
      // ---------------------------------------------------第一种方式-----------------------------------------------------------
      // 方式一：透视投影相机
      // 设置透视投影的相机,默认情况下相机的上方向为Y轴，右方向为X轴，沿着Z轴朝里
      // 参数1：fov表示摄像机视锥体垂直视野角度，最小值为0，最大值为180，默认值为50，实际项目中一般都定义45，因为45最接近人正常睁眼角度；
      // 参数2：aspect表示摄像机视锥体长宽比，默认长宽比为1，即表示看到的是正方形，实际项目中使用的是屏幕的宽高比；
      // 参数3：near表示摄像机视锥体近端面，这个值默认为0.1，实际项目中都会设置为1；
      // 参数4：far表示摄像机视锥体远端面，默认为2000，这个值可以是无限的，说的简单点就是我们视觉所能看到的最远距离
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
      // 透视投影相机的位置和position，up，lookAt有关系。
      // position用来指定相机在三维坐标中的位置，
      // up用来指定相机拍摄时相机头顶的方向，
      //lookAt表示相机拍摄时指向的中心点
      // 设置相机的位置坐标
      camera.position.set(50, 1500, 2000);
      //设置相机的上为「x」轴方向
      camera.up.x = 0;
      //设置相机的上为「y」轴方向
      camera.up.y = 1;
      //设置相机的上为「z」轴方向
      camera.up.z = 0;
      //设置视野的中心坐标
      camera.lookAt({ x: 0, y: 0, z: 0 });

      // ---------------------------------------------------第二种方式-----------------------------------------------------------
      // 方式二：正投影相机
      // 略
    }

    /*
     * 初始化光源
     *
     * OpenGL（WebGL）的三维空间中，存在点光源和聚光灯两种类型。
     * 而且，作为点光源的一种特例还存在平行光源(无线远光源)。
     * 另外，作为光源的参数还可以进行 [环境光] 等设置。 作为对应，
     * Three.js中可以设置 [点光源(Point Light)] [聚光灯(Spot Light)] [平行光源(Direction Light)]，
     * 和 [环境光(Ambient Light)]。 和OpenGL一样、在一个场景中可以设置多个光源。 基
     * 本上，都是环境光和其他几种光源进行组合。
     * 如果不设置环境光，那么光线照射不到的面会变得过于黑暗。
     *
     * 本文中首先按照下面的步骤设置模拟远处类似太阳的光源和平行光源
     * 1、声明全局变量(对象)
     * 2、设置平行光源
     * 3、设置光源向量
     * 4、追加光源到场景
     *
     * 这里我们用「DirectionalLight」类声明一个叫 [light] 的对象来代表模拟远处类似太阳的光源
     */
    function initLight() {
      // 模拟远处类似太阳的光源
      // 第一个参数 Hex:光的颜色
      // 第二个参数 Intensity：光源的强度，默认是1.0，如果为0.5，则强度是一半，意思是颜色会淡一些
      // 第三个参数 Distance：光线的强度，从最大值衰减到0，需要的距离。
      //      默认为0，表示光不衰减，如果非0，则表示从光源的位置到Distance的距离，光都在线性衰减。到离光源距离Distance时，光源强度为0.
      var directionalLight = new THREE.DirectionalLight(0xffffff, 0.3);
      // directionalLight.color (可选参数) 16进制表示光的颜色。 缺省值为 0xffffff (白色)
      // HSL是一种将RGB色彩模型中的点在圆柱坐标系中的表示法。这两种表示法试图做到比基于笛卡尔坐标系的几何结构RGB更加直观。
      // HSL即色相、饱和度、亮度（英语：Hue, Saturation, Lightness）。
      // 1、色相（H）是色彩的基本属性，就是平常所说的颜色名称，如红色、黄色等。
      // 2、饱和度（S）是指色彩的纯度，越高色彩越纯，低则逐渐变灰，取0-100%的数值。
      // 3、明度（V），亮度（L），取0-100%。
      directionalLight.color.setHSL(0.1, 1, 0.95);
      // 设置光源向量
      directionalLight.position.set(0, 200, 0).normalize();
      // 追加光源到场景
      scene.add(directionalLight);
      // 平行光：AmbientLight,影响整个场景的光源
      var ambient = new THREE.AmbientLight(0xffffff, 1);
      ambient.position.set(0, 0, 0);
      scene.add(ambient);
    }

    /*
     * 初始化性能插件
     *
     * JavaScript性能监控器，同样也可以测试webgl的渲染性能
     */
    function initStats() {
      var stats = new Stats();
      stats.domElement.style.position = 'absolute';
      stats.domElement.style.left = '0px';
      stats.domElement.style.top = '0px';
      document.body.appendChild(stats.domElement);
      return stats;
    }

    /*
     * 初始化渲染器
     *
     * 三维空间里的物体映射到二维平面的过程被称为三维渲染
     * 一般来说我们把进行渲染操作的软件叫做渲染器
     * 具体来说要进行下面这些处理:
     * 1、声明全局变量（对象）
     * 2、获取画布「canvas-frame」的高宽
     * 3、生成渲染器对象（属性：抗锯齿效果为设置有效）
     * 4、指定渲染器的宽高（窗口的文档显示区大小一致）
     * 5、追加 【canvas】 元素到 【body】 元素中
     * 6、设置渲染器的清除色(clearColor)
     *
     * 注意：需要注意的是，我们并不需要写一个<canvas>标签，我们只需要定义好盛放canvas的div就可以，canvas是three.js动态生成的
     */
    function initRenderer() {
      // ---------------------------------------------------第一种方式-----------------------------------------------------------
      // 第一种方式：窗口的文档显示区的宽度和高度
      // 生成渲染器对象（属性：抗锯齿效果为设置有效）
      renderer = new THREE.WebGLRenderer({
        antialias: true
      });
      // 指定渲染器的高宽（和画布框大小一致）
      renderer.setSize(window.innerWidth, window.innerHeight);
      //追加 【canvas】 元素到 body 元素中。
      document.body.appendChild(renderer.domElement);
      // 设置canvas背景色(clearColor)，比如更改为：0x4682B4
      renderer.setClearColor(0x000000, 1.0);

      // ---------------------------------------------------第二种方式-----------------------------------------------------------
      // 第二种方式：自定义显示容器宽高，如果使用如下方式，需要在 body 标签下添加: <div id="canvas3d" style="width: 800px;height: 500px;"></div>
      // var width = document.getElementById('canvas3d').clientWidth;
      // var height = document.getElementById('canvas3d').clientHeight;
      // renderer=new THREE.WebGLRenderer({antialias:true});
      // renderer.setSize(width, height );
      // document.getElementById('canvas3d').appendChild(renderer.domElement);
      // renderer.setClearColor(0x000000, 1.0);
    }

    /*
     * 初始化轨迹球控件
     */
    function initControls() {
      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.5;
      // 视角最小距离
      controls.minDistance = 100;
      // 视角最远距离
      controls.maxDistance = 3000;
      // 最大角度
      // controls.maxPolarAngle = Math.PI / 2.2;
      controls.maxPolarAngle = Math.PI;
      controls.target = new THREE.Vector3(50, 50, 0);
    }

    /*
     * 写一个主函数，进行初始化
     */
    function init() {
      // 初始化场景
      initScene();
      // 初始化相机
      initCamera();
      // 初始化渲染器
      initRenderer();
      // 初始化模型
      initContent();
      // 初始化灯光
      initLight();
      // 初始化轨迹球控件
      initControls();

      document.addEventListener('resize', onWindowResize, false);
    }

    /*
     * 窗口变动触发的方法
     */
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    /*
     * 动画
     */
    function animate() {
      // 实现动画效果的方法比较多，Javascript 中可以通过定时器 setTimeout 来实现，
      // css3 可以使用 transition 和 animation 来实现，html5 中的 canvas 也可以实现。除此之外，html5 还提供一个专门用于请求动画的API，
      // 那就是 requestAnimationFrame，顾名思义就是请求动画帧。
      // 屏幕刷新频率：屏幕每秒出现图像的次数。普通笔记本为60Hz
      // 动画原理：计算机每16.7ms刷新一次，由于人眼的视觉停留，所以看起来是流畅的移动。
      // setTimeout：通过设定间隔时间来不断改变图像位置，达到动画效果。但是容易出现卡顿、抖动的现象；
      // 原因是：1、settimeout任务被放入异步队列，只有当主线程任务执行完后才会执行队列中的任务，因此实际执行时间总是比设定时间要晚；
      //        2、settimeout的固定时间间隔不一定与屏幕刷新时间相同，会引起丢帧。
      // requestAnimationFrame：优势：由系统决定回调函数的执行时机。60Hz的刷新频率，
      // 那么每次刷新的间隔中会执行一次回调函数，不会引起丢帧，不会卡顿
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
      update();
    }

    // 更新控件
    function update() {
      stats.update();
      controls.update();
      // 借助tween.js快速创建补间动画，可以非常方便的控制机械、游戏角色运动
      TWEEN.update();
    }

    //创建地板
    function createFloor() {
      var loader = new THREE.TextureLoader();
      loader.load('../ThreeJs/images/floor.jpg', function (texture) {
        texture.wrapS = THREE.RepeatWrapping;
        texture.wrapT = THREE.RepeatWrapping;
        // uv两个方向纹理重复数量
        texture.repeat.set(10, 10);
        var floorGeometry = new THREE.BoxGeometry(2600, 1400, 1);
        var floorMaterial = new THREE.MeshBasicMaterial({
          map: texture,
        });
        var floor = new THREE.Mesh(floorGeometry, floorMaterial);
        floor.rotation.x = -Math.PI / 2;
        floor.name = "地面";
        scene.add(floor);
      });
    }

    // 初始化模型
    function initContent() {
      // 创建地板
      createFloor();
    }

    // 初始化
    init();
    animate();
  </script>
</body>
</html>